#include "enc28j60.h"
#include "usart1.h"

#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_spi.h"

static void enc28j60_gpio_init(void);
static void enc28j60_spi_init(void);

static unsigned char enc28j60_spi_readwrite(unsigned char);

static unsigned char enc28j60_read(unsigned char, unsigned char);
static void enc28j60_write(unsigned char, unsigned char, unsigned char);

static void enc28j60_bank(unsigned char);

static unsigned char enc28j60_reg_read(unsigned char);
static void enc28j60_reg_write(unsigned char, unsigned char);
static void enc28j60_reg_bitset(unsigned char, unsigned char);
static void enc28j60_reg_bitclr(unsigned char, unsigned char);

static unsigned char enc28j60_bank_select = 0x0;
static unsigned int enc28j60_next_packet = 0x0;

static void enc28j60_gpio_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_WriteBit(GPIOB, GPIO_Pin_12, (BitAction)(1));
}

static void enc28j60_spi_init(void)
{
	SPI_InitTypeDef  SPI_InitStructure;

	SPI_Cmd(SPI2, DISABLE);

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI2, &SPI_InitStructure);

	SPI_Cmd(SPI2, ENABLE);
}

static unsigned char enc28j60_spi_readwrite(unsigned char data)
{
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
	SPI_I2S_SendData(SPI2, data);
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
	return SPI_I2S_ReceiveData(SPI2);
}

static void enc28j60_mem_write(unsigned char *data, unsigned int len)
{
	int i = 0;
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
	enc28j60_spi_readwrite(ENC28J60_CTL_WBM);
	for(i = 0; i < len; i++)
	{
		enc28j60_spi_readwrite(data[i]);
	}
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

static void enc28j60_mem_read(unsigned char *data, unsigned int len)
{
	int i = 0;
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
	enc28j60_spi_readwrite(ENC28J60_CTL_PBM);
	for(i = 0; i < len; i++)
	{
		data[i] = enc28j60_spi_readwrite(0);
	}
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

static unsigned char enc28j60_read(unsigned char op, unsigned char address)
{
	unsigned char dat = 0;
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
	enc28j60_spi_readwrite(op | address & 0x1F);
	dat = enc28j60_spi_readwrite(0x0);
	if(address & 0x80)
	{
		dat = enc28j60_spi_readwrite(0x0);
	}
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
	return dat;
}

static void enc28j60_write(unsigned char op, unsigned char address, unsigned char data)
{
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
	enc28j60_spi_readwrite(op | address);
	enc28j60_spi_readwrite(data);
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

static void enc28j60_bank(unsigned char bank)
{
	if(bank != enc28j60_bank_select)
	{
		enc28j60_write(ENC28J60_CTL_BFC, ENC28J60_ECON1 & 0x1F, 0x3);
		enc28j60_write(ENC28J60_CTL_BFS, ENC28J60_ECON1 & 0x1F, bank);
		enc28j60_bank_select = bank;
	}
}

static unsigned char enc28j60_reg_read(unsigned char address)
{
	enc28j60_bank((address >> 0x5) & 0x3);
	return enc28j60_read(ENC28J60_CTL_RCR, address & 0x9F);
}

static void enc28j60_reg_write(unsigned char address, unsigned char data)
{
	enc28j60_bank((address >> 0x5) & 0x3);
	enc28j60_write(ENC28J60_CTL_WCR, address & 0x1F, data);
}

static void enc28j60_reg_bitset(unsigned char address, unsigned char mask)
{
	enc28j60_bank((address >> 0x5) & 0x3);
	enc28j60_write(ENC28J60_CTL_BFS, address & 0x1F, mask);
}

static void enc28j60_reg_bitclr(unsigned char address, unsigned char mask)
{
	enc28j60_bank((address >> 0x5) & 0x3);
	enc28j60_write(ENC28J60_CTL_BFC, address & 0x1F, mask);
}

static void enc28j60_phy_write(unsigned char phy_address, unsigned int data)
{
	enc28j60_reg_write(ENC28J60_MIREGADR, phy_address);
	enc28j60_reg_write(ENC28J60_MIWRL, data);
	enc28j60_reg_write(ENC28J60_MIWRH, data >> 0x8);
	while(enc28j60_reg_read(ENC28J60_MISTAT) & ENC28J60_MISTAT_BUSY);
}

static int enc28j60_data_send(unsigned char *data, unsigned int len)
{
	if(0 >= len && ENC28J60_MAX_FRAMELEN <= len)
	{
		return -1;
	}

	enc28j60_reg_write(ENC28J60_EWRPTL, TXSTART_INIT & 0xFF);
	enc28j60_reg_write(ENC28J60_EWRPTH, TXSTART_INIT >> 0x8);

	enc28j60_reg_write(ENC28J60_ETXNDL, (TXSTART_INIT + len) & 0xFF);
	enc28j60_reg_write(ENC28J60_ETXNDH, (TXSTART_INIT + len) >> 0x8);

	enc28j60_write(ENC28J60_CTL_WBM, 0, 0x0);

	enc28j60_mem_write(data, len);

	enc28j60_reg_bitset(ENC28J60_ECON1, ENC28J60_ECON1_TXRTS);
	
	if(enc28j60_reg_read(ENC28J60_EIR) & ENC28J60_EIR_TXERIF)
	{
		enc28j60_reg_bitclr(ENC28J60_ECON1, ENC28J60_ECON1_TXRTS);
		return -1;
	}
	else
	{
		while(enc28j60_reg_read(ENC28J60_ECON1) & ENC28J60_ECON1_TXRTS);
		return 0;
	}
}

static int enc28j60_data_recv(unsigned char* packet, unsigned int maxlen)
{
	unsigned int rxstat = 0;
	unsigned int len = 0;

	if(0 == enc28j60_reg_read(ENC28J60_EPKTCNT))
	{
		return 0;
	}

	enc28j60_reg_write(ENC28J60_ERDPTL, (enc28j60_next_packet));
	enc28j60_reg_write(ENC28J60_ERDPTH, (enc28j60_next_packet) >> 8);

	enc28j60_next_packet = enc28j60_read(ENC28J60_CTL_PBM, 0);
	enc28j60_next_packet |= enc28j60_read(ENC28J60_CTL_PBM, 0) << 8;

	len = enc28j60_read(ENC28J60_CTL_PBM, 0);
	len |= enc28j60_read(ENC28J60_CTL_PBM, 0) << 8;

	len -= 4;

	rxstat = enc28j60_read(ENC28J60_CTL_PBM, 0);
	rxstat |= enc28j60_read(ENC28J60_CTL_PBM, 0) << 8;

	if(maxlen - 1 < len)
	{
		len = maxlen - 1;
	}

	if((rxstat & 0x80) == 0)
	{
		len = 0;
	}
	else
	{
		enc28j60_mem_read(packet, len);
	}
	enc28j60_reg_write(ENC28J60_ERXRDPTL, (enc28j60_next_packet));
	enc28j60_reg_write(ENC28J60_ERXRDPTH, (enc28j60_next_packet) >> 8);

	enc28j60_reg_bitset(ENC28J60_ECON2, ENC28J60_ECON2_PKTDEC);
	return(len);
}

static void enc28j60_reset(void)
{
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
	enc28j60_spi_readwrite(ENC28J60_CTL_SC);
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
	while(!(enc28j60_reg_read(ENC28J60_ESTAT) & 0x1));
	enc28j60_bank_select = 0;
}

static void enc28j60_fifo_init(void)
{
	enc28j60_next_packet = RXSTART_INIT;

	enc28j60_reg_write(ENC28J60_ERXSTL, RXSTART_INIT & 0xFF);
	enc28j60_reg_write(ENC28J60_ERXSTH, RXSTART_INIT >> 0x8);

	enc28j60_reg_write(ENC28J60_ERXRDPTL, RXSTART_INIT & 0xFF);
	enc28j60_reg_write(ENC28J60_ERXRDPTH, RXSTART_INIT >> 0x8);

	enc28j60_reg_write(ENC28J60_ERXNDL, RXSTOP_INIT & 0xFF);
	enc28j60_reg_write(ENC28J60_ERXNDH, RXSTOP_INIT >> 0x8);

	enc28j60_reg_write(ENC28J60_ETXSTL, TXSTART_INIT & 0xFF);
	enc28j60_reg_write(ENC28J60_ETXSTH, TXSTART_INIT >> 0x8);

	enc28j60_reg_write(ENC28J60_ETXNDL, TXSTOP_INIT & 0xFF);
	enc28j60_reg_write(ENC28J60_ETXNDH, TXSTOP_INIT >> 0x8);
}

static void enc28j60_mac_addr_init(unsigned char *mac_addr)
{
	enc28j60_reg_write(ENC28J60_MAADR0, mac_addr[0]);
	enc28j60_reg_write(ENC28J60_MAADR1, mac_addr[1]);
	enc28j60_reg_write(ENC28J60_MAADR2, mac_addr[2]);
	enc28j60_reg_write(ENC28J60_MAADR3, mac_addr[3]);
	enc28j60_reg_write(ENC28J60_MAADR4, mac_addr[4]);
	enc28j60_reg_write(ENC28J60_MAADR5, mac_addr[5]);
}

static void enc28j60_other_init(void)
{
	enc28j60_reg_write(ENC28J60_ERXFCON, ENC28J60_ERXFCON_UCEN | ENC28J60_ERXFCON_CRCEN | ENC28J60_ERXFCON_PMEN);
	
	enc28j60_reg_write(ENC28J60_EPMM0, 0x3F);
	enc28j60_reg_write(ENC28J60_EPMM1, 0x30);

	enc28j60_reg_write(ENC28J60_EPMCSL, 0xF9);
	enc28j60_reg_write(ENC28J60_EPMCSH, 0xF7);

	enc28j60_reg_write(ENC28J60_MACON1, ENC28J60_MACON1_MARXEN | ENC28J60_MACON1_TXPAUS | ENC28J60_MACON1_RXPAUS);
	enc28j60_reg_write(ENC28J60_MACON2, 0x0);
	enc28j60_reg_bitset(ENC28J60_MACON3, ENC28J60_MACON3_PADCFG0 | ENC28J60_MACON3_TXCRCEN | ENC28J60_MACON3_FRMLNEN | ENC28J60_MACON3_FULDPX);
	
	enc28j60_reg_write(ENC28J60_MAIPGL, 0x12);
	enc28j60_reg_write(ENC28J60_MAIPGH, 0xC);

	enc28j60_reg_write(ENC28J60_MABBIPG, 0x15);

	enc28j60_reg_write(ENC28J60_MAMXFLL, ENC28J60_MAX_FRAMELEN & 0xFF);
	enc28j60_reg_write(ENC28J60_MAMXFLH, ENC28J60_MAX_FRAMELEN >> 0x8);

	enc28j60_phy_write(ENC28J60_PHCON1, ENC28J60_PHCON1_PDPXMD);
	enc28j60_phy_write(ENC28J60_PHCON2, ENC28J60_PHCON2_HDLDIS);

	//enc28j60_bank(ENC28J60_BANK0);

	enc28j60_reg_bitclr(ENC28J60_EIE, 0x1);

	enc28j60_reg_bitset(ENC28J60_ECON1, 0x4);

	enc28j60_phy_write(ENC28J60_PHLCON, 0x476);
}

static ENC28J60_INTERFACE_T _enc28j60_obj = {
	.read = enc28j60_reg_read,
	.write = enc28j60_reg_write,
	.send = enc28j60_data_send,
	.recv = enc28j60_data_recv
};

void enc28j60_init(unsigned char *mac_addr)
{
	enc28j60_gpio_init();
	enc28j60_spi_init();

	enc28j60_reset();

	enc28j60_fifo_init();
	
	enc28j60_mac_addr_init(mac_addr);

	enc28j60_other_init();
}

void enc28j60_get_interface(ENC28J60_INTERFACE_P *obj)
{
	*obj = &_enc28j60_obj;
}
